package es.unileon.rnag.jgen;

import java.util.*;
import es.unileon.rnag.jgen.operadores.*;

/**
 * Clase que representa un algoritmo gen&eacute;tico
 * @author Abel Mayorga Gonz&aacute;lez
 * @author Sergio Fern&aacute;ndez Mart&iacute;nez
 * @version 1.0
 */
public class AlgoritmoGenetico {
	private List<Cromosoma> _poblacion = new ArrayList<Cromosoma>();
	private Estrategia _estrategia;
	private int _generaciones;
	private int _nIndividuos;
	private Parada _parada;
	private Cromosoma _mejor = null;
	
	//Construye un comparador que permite ordenar los cromosomas de mejor a peor
	private static Comparator<Cromosoma> _comparador = new Comparator<Cromosoma>(){
		public int compare(Cromosoma o1, Cromosoma o2) {
			double apt1 = o1.getAptitud();
			double apt2 = o2.getAptitud();
			if(apt1<apt2){
				return 1;
			} else if(apt1==apt2){
				return 0;
			} else {
				return -1;
			}
		}
	};
	
	/**
	 * Constructor
	 * @param tipoCromosoma: Clase de cromosoma que va a usar el algoritmo gen&eacute;tico
	 * @param e: Estrategia que va a seguir el algoritmo gen&eacute;tico
	 * @param poblacion: Poblaci&oacute;n inicial del algoritmo gen&eacute;tico
	 * @param generaciones: N&uacute;mero de generaciones m&aacute;ximas del algoritmo gen&eacute;tico
	 */
	public AlgoritmoGenetico(Class<? extends Cromosoma> tipoCromosoma, Estrategia e, int poblacion, int generaciones){
		this(tipoCromosoma, e, new ParadaNula(), poblacion, generaciones);
	}
	
	/**
	 * Constructor 
	 * @param tipoCromosoma: Clase de cromosoma que va a usar el algoritmo gen&eacute;tico
	 * @param e: Estrategia que va a seguir el algoritmo gen&eacute;tico
	 * @param p: Condici&oacute;n de parada del algoritmo gen&eacute;tico
	 * @param poblacion: Poblaci&oacute;n inicial del algoritmo gen&eacute;tico
	 * @param generaciones: N&uacute;mero de generaciones m&aacute;ximas del algoritmo gen&eacute;tico
	 */
	public AlgoritmoGenetico(Class<? extends Cromosoma> tipoCromosoma, Estrategia e, Parada p, int poblacion, int generaciones){
		_estrategia = e;
		_parada = p;
		_generaciones = generaciones;
		_nIndividuos = poblacion;
		try{
			//Instancia los cromosomas
			for(int i = 0; i < _nIndividuos; i++){
				_poblacion.add(tipoCromosoma.newInstance());
			}
		} catch (InstantiationException exc){
			exc.printStackTrace();
		} catch (IllegalAccessException exc){
			exc.printStackTrace();
		}
	}
	
	/**
	 * Hace que el algoritmo evolucione hasta terminar
	 */
	public void evolucionar(){
		Collections.sort(_poblacion, _comparador); //Ordena los cromosomas de mejor a peor
		_mejor = _poblacion.get(0).clonar(); //Se queda con el mejor
		//Itera sobre el numero de generaciones
		for(int i = 0; i < _generaciones; i++){
			//Si se cumple la condicion de parada se termina
			if(_parada.comprobarParada(_poblacion,i)){
				break;
			}
			//Se obtiene la siguiente generacion
			_poblacion = _estrategia.siguientePoblacion(_poblacion,i);
			//Se ordena
			Collections.sort(_poblacion, _comparador);
			//Se obtiene el mejor de la generacion
			Cromosoma mejorGeneracion = _poblacion.get(0);
			//Si es mejor que el que habia antes, se almacena
			if(_mejor.getAptitud()< mejorGeneracion.getAptitud()){
				_mejor = mejorGeneracion.clonar();
			}
		}
	}
	
	/**
	 * @return El mejor cromosoma
	 */
	public Cromosoma getMejor(){
		return _mejor;
	}
	
}
